/*
 * Copyright 2009-2010 Nanjing RedOrange ltd (http://www.red-orange.cn)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package redora.client.mvp;

import com.google.gwt.activity.shared.AbstractActivity;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.SingleSelectionModel;
import redora.client.Persistable;
import redora.client.ui.ActiveWidget;
import redora.client.ui.ImageActionCell;
import redora.client.util.ClientUtil;
import redora.client.util.GWTViewUtil;

import java.util.logging.Logger;

import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static redora.client.Persistable.Scope.Table;

/**
 * Browsing activity: looking through all records of given pojo. The display is implemented
 * by {@link redora.client.mvp.TableViewImpl}. Ths activity will add {@link redora.client.mvp.FindEvent} to bind it to the
 * retrieval of the appropriate FindAll finder. Also each record will be bound with a
 * {@link redora.client.mvp.FetchEvent}, so update of each of the records will automatically be updated to
 * the view.
 *
 * @param <T>
 * @author Nanjing RedOrange (www.red-orange.cn)
 */
public class BrowseActivity<T extends Persistable> extends AbstractActivity implements TableView.Presenter, ActiveWidget, ProvidesKey<T> {
    static Logger l = Logger.getLogger("BrowseActivity");

    final ClientFactory clientFactory;
    EventBus eventBus;
    TableView display;
    final CellTable<T> table;

    ClientUtil<T> util;
    final Class<T> cls;
    String finderUrl;
    /** True if there is new data expected. */
    boolean isLoading = false;

    final ListDataProvider<T> dataProvider = new ListDataProvider<T>();
    final SingleSelectionModel<T> selectionModel;
    final ProvidesKey<T> keyProvider;

    public BrowseActivity(Class<T> cls, ClientFactory clientFactory) {
        this.cls = cls;
        this.clientFactory = clientFactory;

        keyProvider = new ProvidesKey<T>() {
            @Override
            public Object getKey(T item) {
                return (item == null) ? null : item.getId();
            }
        };
        selectionModel = new SingleSelectionModel<T>(keyProvider);
        table = new CellTable<T>(keyProvider);
        table.setSelectionModel(selectionModel);

        l.log(INFO, "Initialized for " + cls);
    }

    @Override
    public void start(AcceptsOneWidget panel, EventBus eventBus) {
        this.eventBus = eventBus;
        util = clientFactory.getLocator().locateUtil(cls);

        display = new TableViewImpl(cls, clientFactory.getLocator(), table);
        display.setPresenter(this);

        Column<T, T> column = new Column<T, T>(new ImageActionCell<T>("images/cross.png", new ImageActionCell.Delegate<T>() {

            @Override
            public void execute(T object) {
                 clientFactory.getLocator().locateService(cls).delete(object);
            }
        })) {
            @Override
            public T getValue(T object) {
                return object;
            }
        };
        table.addColumn(column);
        display.initTable(); //Before this you can play with the display.fields.
        panel.setWidget(display.asWidget());

        dataProvider.addDataDisplay(display.getData());

        bind();
        Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
            @Override
            public void execute() {
                load();
            }
        });
        l.log(FINE, "Widget is started and bind.");
    }

    @Override
    public Object getKey(T item) {
        return selectionModel.getKey(item);
    }

    @Override
    public void load() {
        isLoading  = true;
        BrowsePlace here = (BrowsePlace) clientFactory.getPlaceController().getWhere();
        finderUrl = clientFactory.getLocator().locateService(cls).url(here.defaultFinderOrdinal);
        l.log(INFO, "Started " + here + ", my finder will be " + finderUrl);
        clientFactory.getLocator().locateService(cls).finder(finderUrl, Table);
    }

    private void bind() {
        table.addDomHandler(new DoubleClickHandler() {
            @Override
            public void onDoubleClick(DoubleClickEvent event) {
                clientFactory.getPlaceController().goTo(new EditPlace(cls, selectionModel.getSelectedObject().getId()));
            }
        }, DoubleClickEvent.getType());
        eventBus.addHandler(FetchEvent.TYPE,
                new FetchEvent.Handler() {
                    @Override
                    public void onSuccess(FetchEvent event) {
                        if (event.hasMe(cls, clientFactory.getLocator().locateService(cls).cache(finderUrl))) {
                            for (Long id : event.union(clientFactory.getLocator().locateService(cls).cache(finderUrl))) {
                                //Add or replace
                                if (dataProvider.getList().contains((T) event.getPojo(id))) {
                                    dataProvider.getList().set(dataProvider.getList().indexOf((T) event.getPojo(id)), (T) event.getPojo(id));
                                } else {
                                    dataProvider.getList().add((T) event.getPojo(id));
                                }
                            }
                        }
                        //A little inefficient, but it works. The refresh is needed for active widgets in the
                        //table, like LazyCell. You can filter for any specific related object by using:
                        // if (event.cls.equals(TheOtherClass.class))
                        //   dataProvider.refresh();
                        if (!isLoading)
                            dataProvider.refresh();
                    }
                });
        eventBus.addHandler(FindEvent.TYPE,
                new FindEvent.Handler() {
                    @Override
                    public void onSuccess(FindEvent event) {
                        l.log(INFO, "Received FindEvent for " + cls + ": " + event.finderKey);
                        if (event.wasMe(cls, finderUrl)) {
                            l.log(INFO, "Received matching FindEvent for " + cls + ": " + event.finderKey);
                            isLoading = event.hasMore;
                            if (event.step == 0)
                                dataProvider.getList().clear();
                            clientFactory.getLocator().locateService(cls).findById(event.results, Table);
                        } else {
                            //A little inefficient, but it works. The refresh is needed for active widgets in the
                            //table, like ListPopupCell. You can filter for any specific FindEvent by using:
                            // if (event.cls.equals(TheOtherClass.class) && event.finder == TheOtherClass.DefaultFinder.FindByMyId.ordinal())
                            //   dataProvider.refresh();
                            if (!isLoading)
                                dataProvider.refresh();
                        }
                    }
                });
        eventBus.addHandler(DeleteEvent.TYPE,
                new DeleteEvent.Handler() {

                    @Override
                    public void onSuccess(DeleteEvent event) {
                        GWTViewUtil.displayInfo("Delete " + cls + " successful!");//TODO i18n
                        clientFactory.getLocator().locateService(cls).finder(finderUrl, Table);
                    }

                    @Override
                    public void onViolation(DeleteEvent event) {

                    }
                });
        l.log(INFO, "Bind the events to " + eventBus);
    }
}
